//
//  File.swift
//  
//
//  Created by Flutter on 2021/7/9.
//

import Foundation
import Reachability
import Standard

public enum ReachabilityMonitor {}

extension ReachabilityMonitor: TypeName {}

private extension ReachabilityMonitor {
    static let lock = ReadWriteLock()
    static var observers = [AnyReachabilityObserver]()
    static let queue = DispatchQueue(label: typeName, qos: .background)
    static let reachability: Reachability = {
        do {
            let reachability = try Reachability(hostname: "www.baidu.com", queueQoS: .background, targetQueue: queue, notificationQueue: queue)
            return reachability
        } catch {
            fatalError("\(error)")
        }
    }()
}

public extension ReachabilityMonitor {
    static func start() {
        do {
            reachability.whenReachable = whenReachable
            reachability.whenUnreachable = whenUnreachable
            try reachability.startNotifier()
        } catch {
            fatalError("\(error)")
        }
    }

    static func stop() {
        reachability.stopNotifier()
    }

    static var connection: Reachability.Connection {
        reachability.connection
    }
}

public extension ReachabilityMonitor {
    static func add(observer: ReachabilityObservable) {
        observers.append(AnyReachabilityObserver(observer: observer))
    }
}

private extension ReachabilityMonitor {
    static func flatten() {
        lock.withWriterLockVoid { observers = observers.filter { !$0.isRemovable } }
    }

    static func whenReachable(reachability: Reachability) {
        flatten()
        lock.withReaderLockVoid { observers.forEach { handleConnect(observer: $0.observable, connect: reachability.connection) } }
    }

    static func whenUnreachable(reachability: Reachability) {
        flatten()
        lock.withReaderLockVoid { observers.forEach { handleConnect(observer: $0.observable, connect: reachability.connection) } }
    }

    static func handleConnect(observer: ReachabilityObservable?, connect: Reachability.Connection) {
        switch connect {
        case .none:
            observer?.reachabilityObserverConnectionUnavailable()
        case .unavailable:
            observer?.reachabilityObserverConnectionUnavailable()
        case .wifi:
            observer?.reachabilityObserverConnectionWifi()
        case .cellular:
            observer?.reachabilityObserverConnectionCellular()
        }
    }
}

